/*
 * @Author: lizhil
 * @Date: 2020-11-22 19:31:43
 * @Last Modified by: lizhil
 * @Last Modified time: 2021-12-16 22:05:03
 */
const { Controller } = require('egg')
const xlsx = require('node-xlsx')

/**
 * API
 * https://eggjs.org/zh-cn/basics/router.html#restful-%E9%A3%8E%E6%A0%BC%E7%9A%84-url-%E5%AE%9A%E4%B9%89
 */
class BaseController extends Controller {
  // 默认唯一ID
  get key() {
    return '_id'
  }

  // 默认排序
  get sort() {
    return {
      [this.key]: -1,
    }
  }

  // 分页查询
  get findPage() {
    return true
  }

  // 批量操作
  get batchAction() {
    return true
  }

  // 过滤器
  get filter() {
    return {}
  }

  // 列表接口
  async index() {
    this.isIndex = true

    const { ctx } = this
    const { query, helper } = ctx

    let options = {}

    // 列表查询前
    if (this.beforeIndex) options = await this.beforeIndex()

    options = {
      ...options,
      ...this.findOptions,
    }

    const filter = {
      ...this.where, // 兼容老项目写法
      ...this.filter,
      ...options.where, // 兼容老项目写法
      ...options.filter,
    }

    if (this.where) console.warn('[ApiController] beforeIndex where 已废弃，请使用filter')
    if (options.where) console.warn('[ApiController] options.where 已废弃，请使用filter')

    // 匹配查询
    if (this.searchKey) {
      this.searchKey.forEach((key) => {
        if (query[key]) filter[key] = query[key]
      })
    }

    // 模糊查询
    if (this.likeKey) {
      this.likeKey.forEach((key) => {
        if (query[key] && !query[key] == '') {
          const keyword = query[key].trim()
          const reg = new RegExp(helper.toMongoRegex(keyword), 'i') // i 不区分大小写
          filter[key] = reg
        }
      })
    }

    let returnData
    let list

    // 分页查询
    if (this.findPage) {
      if (query.page && query.page != '') options.page = Number(query.page)
      if (query.pageSize && query.pageSize != '') options.pageSize = Number(query.pageSize)

      returnData = await this.Model.findPage(filter, options)
      list = returnData.list
    }
    // 查询所有
    else {
      let find = this.Model.find(filter)

      Object.keys(options).map((key) => {
        if (find[key]) find = find[key](options[key])
      })

      list = returnData = await find
    }

    // 列表查询后
    if (this.afterIndex) await this.afterIndex(returnData)

    // 格式化数据，使用formatData会自动将自动执行toJSON
    if (list && this.formatData) {
      const newList = []
      await helper.queue(list, async (doc, index) => {
        if (doc.toJSON) doc = doc.toJSON()
        const newDoc = await this.formatData(doc)
        newList[index] = newDoc ? newDoc : doc
      })

      if (newList.length) {
        if (this.findPage) {
          returnData.list = newList
        } else {
          returnData = newList
        }
      }
    }

    if (query.isExport) {
      await this.export(this.findPage ? returnData.list : returnData)
    } else {
      ctx.body = {
        code: 0,
        msg: 'success',
        data: returnData,
      }
    }
  }

  // 详情接口
  async show() {
    this.isShow = true

    const { ctx } = this
    const { params } = ctx

    let filter = {
      [this.key]: params.id,
    }

    if (this.beforeShow) {
      const newFilter = await this.beforeShow(filter)
      if (newFilter) filter = newFilter
    }

    let findOne = this.Model.findOne(filter)

    let doc = await findOne

    if (doc && this.afterShow) {
      let newDoc = await this.afterShow(doc)
      if (newDoc) doc = newDoc
    }

    if (!doc) throw '获取详情失败'

    if (this.formatData) {
      doc = doc.toJSON()
      const newDoc = await this.formatData(doc)
      if (newDoc) doc = newDoc
    }

    ctx.body = {
      code: 0,
      msg: 'success',
      data: doc,
    }
  }

  // 新增接口
  async create() {
    this.isNew = true

    const { ctx } = this
    const { body } = ctx.request

    this.beforeCreate && (await this.beforeCreate())
    this.beforeSave && (await this.beforeSave())

    const doc = await this.Model.create(body)

    this.afterCreate && (await this.afterCreate(doc))
    this.afterSave && (await this.afterSave(doc))

    ctx.body = {
      code: 0,
      msg: '操作成功',
      data: doc,
    }
  }

  // 修改接口
  async update() {
    this.isUpdate = true

    const { ctx } = this
    const { params } = ctx
    const { body } = ctx.request

    const idList = params.id.split(',')

    this.isBatch = idList.length == 1 ? false : true

    if (this.isBatch && !this.batchAction) throw '不可批量操作'

    this.beforeUpdate && (await this.beforeUpdate())
    this.beforeSave && (await this.beforeSave())

    let doc
    if (this.isBatch) {
      await this.Model.updateMany(
        {
          [this.key]: {
            $in: idList,
          },
        },
        body
      )
    } else {
      // 创建配置
      doc = await this.Model.findOneAndUpdate(
        {
          [this.key]: params.id,
        },
        body,
        {
          new: true, // 返回更新后的数据
        }
      )
    }

    this.afterUpdate && (await this.afterUpdate(doc))
    this.afterSave && (await this.afterSave(doc))

    ctx.body = {
      code: 0,
      msg: '操作成功',
      data: doc,
    }
  }

  // 删除接口
  async destroy() {
    const { ctx } = this
    const { params } = ctx

    const idList = params.id.split(',')

    this.isBatch = idList.length == 1 ? false : true

    let filter = {}

    if (this.isBatch) {
      if (!this.batchAction) throw '不可批量操作'

      filter = {
        [this.key]: params.id,
      }
    } else {
      filter = {
        [this.key]: {
          $in: idList,
        },
      }
    }

    if (this.beforeDestroy) {
      let fnData = await this.beforeDestroy(filter)
      if (fnData) filter = fnData
    }

    let returnData
    if (this.isBatch) {
      returnData = await this.Model.deleteMany(filter)
    } else {
      returnData = await this.Model.deleteOne(filter)
    }

    this.afterDestroy && (await this.afterDestroy(returnData))

    ctx.body = {
      code: 0,
      msg: '操作成功',
      data: returnData,
    }
  }

  // 数据复制
  async copy() {
    const { ctx } = this
    const { body } = ctx.request
    const { _id } = body

    const doc = await this.Model.findOne({ _id })
    const newDoc = doc.toJSON()
    delete newDoc._id

    if (body.nameKeys) {
      body.nameKeys.forEach((key) => {
        newDoc[key] += '_复制'
      })
    }

    await this.Model.create(newDoc)

    ctx.body = {
      code: 0,
      msg: 'success',
    }
  }

  // async formatData() {}

  /**
   * 导出
   */
  async export(data) {
    const { ctx } = this

    // 导出结构
    if (!this.exportConfig) throw '未配置导出内容'
    const exportConfig = await this.exportConfig(data)

    // 导出头
    const exportHead = []
    exportConfig.map((item) => {
      exportHead.push(item.name)
    })

    const exportContent = []

    // 导出内容
    for (let i = 0; i < data.length; i++) {
      let item = data[i]
      if (item.toJSON) item = item.toJSON()

      const exportItem = []

      for (let i = 0; i < exportConfig.length; i++) {
        const exportConfigItem = exportConfig[i]
        let value

        if (exportConfigItem.value) {
          value = await exportConfigItem.value(item)
        } else {
          const key = exportConfigItem.key
          value = exportConfigItem.value || item[key]
        }

        if (value) {
          // 对象转字符串
          if (typeof value == 'object') {
            try {
              value = JSON.stringify(value)
            } catch (err) {
              console.log('err', err)
            }
          }
        } else if (value == null || value == undefined) {
          value == ''
        }

        exportItem.push(value)
      }

      exportContent.push(exportItem)
    }

    const buffer = this._buildXlsx([exportHead, ...exportContent])

    ctx.attachment(Date.now() + '.xlsx')
    ctx.body = buffer
    // ctx.body = fs.createReadStream(result.filename);
  }

  async exportConfig(data) {
    const config = []

    for (const key in data) {
      config.push({
        name: key,
        value: key,
      })
    }

    return config
  }

  _buildXlsx(data) {
    const buffer = xlsx.build([
      {
        name: 'sheetName',
        data,
      },
    ])

    return buffer
  }
}
module.exports = BaseController
